// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现对信号处理的运行时支持。
// 
// 大多数同步原语无法从
// 信号处理程序（它不能阻止、分配内存或使用锁）
// 因此，处理程序通过下面的struct sig与处理goroutine 
// 通信。
// 
// sigsend被信号处理程序调用以对新信号排队。
// Go程序调用信号_recv以接收新排队的信号。
// sigsend和signal_recv之间的同步基于sig.state 
// 变量。它可以处于4种状态：sigIdle、sigceigning、sigssending和sigFixup。
// sig接收意味着信号接收在sig上被阻塞。注意，没有新的待处理信号。
// sigssending意味着sig.mask*可能*包含新的挂起信号，
// signal_recv在此状态下不能被阻止。
// sigIdle表示没有新的挂起信号，且信号_recv未被阻止。
// sigFixup是一种暂时状态，它只能作为一个短的
// 从SIGCreceiving然后转换到sigIdle：它是
// 用于确保AllThreadsSyscall（）的mDoFixup（）操作
// 在睡眠的m上发生，等待接收信号。
// 状态之间的转换是通过CAS以原子方式完成的。
// 当信号_recv被解锁时，它将重置sig.Note并重新检查sig.mask。
// 如果多个sigsends和signal_recv同时执行，可能会导致
// 对sig.mask进行不必要的重新检查，但不会导致信号丢失
// 或死锁。

// 开始：构建！计划9 
// /+建造！plan9 

package runtime

import (
	"runtime/internal/atomic"
	_ "unsafe" // 对于go:linkname 
)

// sig处理信号处理程序和os/signal之间的通信。
// 除inuse和recv字段外，这些字段是以原子方式访问的。
// 
// 一次只由一个goroutine在
// 写入需要和忽略的字段；访问由os/signal中的处理程序互斥体控制。
// 字段仅由该goroutine和信号处理程序读取。
// 我们以原子方式访问它们，以最大限度地减少在调用os/signal的goroutine中设置它们
// 和可能在不同线程中运行的信号处理程序
// 之间的争用。这种竞争是不可避免的，因为处理信号和接收信号之间没有联系，但原子指令应该将其最小化。
var sig struct {
	note       note
	mask       [(_NSIG + 31) / 32]uint32
	wanted     [(_NSIG + 31) / 32]uint32
	ignored    [(_NSIG + 31) / 32]uint32
	recv       [(_NSIG + 31) / 32]uint32
	state      uint32
	delivering uint32
	inuse      bool
}

const (
	sigIdle = iota
	sigReceiving
	sigSending
	sigFixup
)

// sigsend将信号从sighandler传递到内部信号传递队列。
// 报告是否发送了信号。否则，调用方通常会使程序崩溃。
// 它是从信号处理程序运行的，因此它的功能受到限制。
func sigsend(s uint32) bool {
	bit := uint32(1) << uint(s&31)
	if s >= uint32(32*len(sig.wanted)) {
		return false
	}

	atomic.Xadd(&sig.delivering, 1)
	// 我们正在信号处理器中运行；“延迟”不可用。

	if w := atomic.Load(&sig.wanted[s/32]); w&bit == 0 {
		atomic.Xadd(&sig.delivering, -1)
		return false
	}

	// 向传出队列添加信号。
	for {
		mask := sig.mask[s/32]
		if mask&bit != 0 {
			atomic.Xadd(&sig.delivering, -1)
			return true // 队列中已存在信号
		}
		if atomic.Cas(&sig.mask[s/32], mask, mask|bit) {
			break
		}
	}

	// 通知接收方队列中有新位。
Send:
	for {
		switch atomic.Load(&sig.state) {
		default:
			throw("sigsend: inconsistent state")
		case sigIdle:
			if atomic.Cas(&sig.state, sigIdle, sigSending) {
				break Send
			}
		case sigSending:
			// 通知已挂起
			break Send
		case sigReceiving:
			if atomic.Cas(&sig.state, sigReceiving, sigIdle) {
				if GOOS == "darwin" || GOOS == "ios" {
					sigNoteWakeup(&sig.note)
					break Send
				}
				notewakeup(&sig.note)
				break Send
			}
		case sigFixup:
			// 无事可做-我们需要等待sigIdle。
			mDoFixupAndOSYield()
		}
	}

	atomic.Xadd(&sig.delivering, -1)
	return true
}

// sigrecvprepreforfixup用于暂时唤醒正在运行的
// signal_recv（）线程，同时阻止它等待信号到达。如果它导致线程唤醒，
// sig.state将按以下顺序运行：sigReceiving->sigFixup 
// ->sigIdle->sigReceiving并继续。（仅在禁用
// GC时调用此函数。）
// go:nosplit 
func sigRecvPrepareForFixup() {
	if atomic.Cas(&sig.state, sigReceiving, sigFixup) {
		notewakeup(&sig.note)
	}
}

// 被调用以接收下一个排队信号。
// 一次只能从一个goroutine调用。
// go:linkname signal\u recv os/signal.signal\u recv 
func signal_recv() uint32 {
	for {
		// 提供本地副本的任何信号。
		for i := uint32(0); i < _NSIG; i++ {
			if sig.recv[i/32]&(1<<(i&31)) != 0 {
				sig.recv[i/32] &^= 1 << (i & 31)
				return i
			}
		}

		// 等待信号发送方提供更新。
	Receive:
		for {
			switch atomic.Load(&sig.state) {
			default:
				throw("signal_recv: inconsistent state")
			case sigIdle:
				if atomic.Cas(&sig.state, sigIdle, sigReceiving) {
					if GOOS == "darwin" || GOOS == "ios" {
						sigNoteSleep(&sig.note)
						break Receive
					}
					notetsleepg(&sig.note, -1)
					noteclear(&sig.note)
					if !atomic.Cas(&sig.state, sigFixup, sigIdle) {
						break Receive
					}
					// 到了这里，代码将再次循环以睡眠
					// 处于接收状态。当另一个
					// 路径。
					// /SIGRECVPREPREFORFIXUP（）
					// 时，将采用此
					// 线程调用了
				}
			case sigSending:
				if atomic.Cas(&sig.state, sigSending, sigIdle) {
					break Receive
				}
			}
		}

		// 将发件人的更新合并到本地副本中。
		for i := range sig.mask {
			sig.recv[i] = atomic.Xchg(&sig.mask[i], 0)
		}
	}
}

// 信号等待直到信号传递机制空闲。
// 这用于确保我们不会因为
// 在禁用信号和接收信号之间的竞争。
// 这假设
// 相关信号的信号传递已被禁用，这里我们只是等待确认
// 所有信号已通过操作系统/信号包传递到用户通道
// 上。
// go:linkname signalWaitUntilIdle os/signal.signalWaitUntilIdle 
func signalWaitUntilIdle() {
	// 虽然我们关心的信号已从
	// sig.want中删除，但可能另一个线程已接收到
	// 一个信号，已从sig.want读取，正在更新sig.mask，
	// 尚未唤醒处理器线程。我们需要等待
	// 直到所有当前信号交付完成。
	for atomic.Load(&sig.delivering) != 0 {
		Gosched()
	}

	// 虽然WaitUntilIdle似乎是这个
	// 函数的正确名称，但我们要查找的状态是sigceiving，而不是
	// sigIdle。sigIdle状态实际上更像是sigProcessing。
	for atomic.Load(&sig.state) != sigReceiving {
		Gosched()
	}
}

// 一次只能从一个goroutine调用。
// go:linkname signal\u enable os/signal.signal\u enable 
func signal_enable(s uint32) {
	if !sig.inuse {
		// 这是第一次调用signal\u enable。初始化。
		sig.inuse = true // 启用信号接收；无法禁用
		if GOOS == "darwin" || GOOS == "ios" {
			sigNoteSetup(&sig.note)
		} else {
			noteclear(&sig.note)
		}
	}

	if s >= uint32(len(sig.wanted)*32) {
		return
	}

	w := sig.wanted[s/32]
	w |= 1 << (s & 31)
	atomic.Store(&sig.wanted[s/32], w)

	i := sig.ignored[s/32]
	i &^= 1 << (s & 31)
	atomic.Store(&sig.ignored[s/32], i)

	sigenable(s)
}

// 一次只能从单个goroutine调用。
// go:linkname signal\u disable os/signal.signal\u disable 
func signal_disable(s uint32) {
	if s >= uint32(len(sig.wanted)*32) {
		return
	}
	sigdisable(s)

	w := sig.wanted[s/32]
	w &^= 1 << (s & 31)
	atomic.Store(&sig.wanted[s/32], w)
}

// 一次只能从一个goroutine调用。
// go:linkname signal\u ignore os/signal.signal\u ignore 
func signal_ignore(s uint32) {
	if s >= uint32(len(sig.wanted)*32) {
		return
	}
	sigignore(s)

	w := sig.wanted[s/32]
	w &^= 1 << (s & 31)
	atomic.Store(&sig.wanted[s/32], w)

	i := sig.ignored[s/32]
	i |= 1 << (s & 31)
	atomic.Store(&sig.ignored[s/32], i)
}

// sigInitIgnored将信号标记为已忽略。这在
// 程序initsig启动时调用。在共享库中，initsig由
// libpreinit调用，因此运行时可能尚未初始化。
// go:nosplit 
func sigInitIgnored(s uint32) {
	i := sig.ignored[s/32]
	i |= 1 << (s & 31)
	atomic.Store(&sig.ignored[s/32], i)
}

// 由信号处理程序检查。
// go:linkname signal\u忽略操作系统/signal.signal\u忽略
func signal_ignored(s uint32) bool {
	i := atomic.Load(&sig.ignored[s/32])
	return i&(1<<(s&31)) != 0
}
